import signal
import urllib.parse

import webview
import pyautogui
import time
import ctypes
import pyperclip
import os
import subprocess
import win32api
import win32con
import win32gui
import threading
import sys
from PIL import Image
import uuid
import hashlib
import requests
import logging
import keyboard
from pystray import Menu, MenuItem, Icon
import actions
import message
import user
import utils
import update
import getHomeWork
import logging
import os
import ght
from datetime import datetime
# 添加单实例检测所需的库
import win32event
import win32process
logging.basicConfig(level=logging.DEBUG,
                    filename='./error.log',
                    filemode='a',
                    format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')

ESC = 0x12
# 定义Alt键的虚拟键码（左右Alt键）
ALT_LEFT = 0xA4
ALT_RIGHT = 0xA5
version = ''
ai_msg = ""
url = 'http://hetaojia.corp.hetao101.com/status?id={0}&crmid={1}'
status = True
current_dir = os.path.dirname(os.path.abspath(__file__))
# 拼接res目录下index.html的路径
html_path = os.path.join(current_dir, 'res', 'index.html')
# 转换为适合webview的URL格式
html_url = f'{html_path.replace(os.sep, "/")}'
hurl=html_path.replace('/', '\\\\')
teachername=""

# 配置日志
def setup_logger():
    # 日志文件路径（同级目录error.log）
    log_path = os.path.join(os.path.dirname(__file__), "error.log")

    # 创建logger
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)  # 设置日志级别

    # 避免重复添加处理器
    if logger.handlers:
        logger.handlers = []

    # 格式器：包含时间、级别、消息
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

    # 文件处理器（写入日志文件）
    file_handler = logging.FileHandler(log_path, encoding='utf-8')
    file_handler.setFormatter(formatter)

    # 控制台处理器（同时输出到终端）
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)

    # 添加处理器
    logger.addHandler(file_handler)
    logger.addHandler(console_handler)

    return logger


logger = setup_logger()

logger.info(hurl)


def get_mac_address():
    mac = uuid.getnode()
    mac_address = ':'.join(['{:02x}'.format((mac >> i) & 0xff) for i in range(0, 48, 8)])
    return mac_address

def hash_mac_address(mac_address):
    sha256_hash = hashlib.sha256(mac_address.encode()).hexdigest()
    return sha256_hash

def save_machine_code(code, crmid):
    while True:
        if not status:
            break
        #response = requests.get(url.format(code, crmid), timeout=10)
        try:
            response = requests.get(url.format(code, crmid), timeout=10)
            #print(response.content.decode('utf-8'))
            if response.content.decode('utf-8') == '1':
                print(response.content.decode('utf-8'))
                window.hide()
            if response.content.decode('utf-8') == '2':
                print(response.content.decode('utf-8'))
                window.show()
            if response.content.decode('utf-8') == '0':
                print(response.content.decode('utf-8'))
                window.destroy()
            #time.sleep(1)
        except requests.exceptions.RequestException as e:
            print(f"请求失败: {e}")


def start_heart(crmid):
    print("启动心跳")
    code = hash_mac_address(get_mac_address())
    sing_process = threading.Thread(target=save_machine_code, kwargs={"code": code, "crmid": crmid})
    sing_process.start()

def copytxt(add):
    file = open(add, "r", encoding='utf-8')
    lines = file.readlines()
    file.close()
    return lines

def on_closed():
    print('pywebview window is closed')
    message.sched.shutdown(wait=False)

def on_closing():
    print('pywebview window is closing')

def on_shown():
    print('pywebview window shown')

def on_minimized():
    print('pywebview window minimized')

def on_maximized():
    print('pywebview window maximized')

def on_restored():
    print('pywebview window restored')

def isPressed(key):
    return bool(ctypes.windll.user32.GetAsyncKeyState(key) & 0x8000)

def on_test():
    print('test')

def qwqf():
    count = 1
    num = 0
    txt_tables = utils.readtxt(select())
    while count:
        try:
            # 检测ESC键或任意Alt键是否被按下
            if isPressed(ESC) or isPressed(ALT_LEFT) or isPressed(ALT_RIGHT):
                count = 0
                break
                print("已通过快捷键终止操作")
            for index in range(len(txt_tables)):
                if txt_tables[index] == '':
                    continue
                time.sleep(0.05)
                actions.clickp('res\searching.png', 0.8)
                time.sleep(0.05)
                pyperclip.copy(txt_tables[index])
                time.sleep(0.3)
                pyautogui.hotkey('ctrl', 'v')
                time.sleep(0.2)
                actions.clickp('res\choose.png', 0.8)
                time.sleep(0.05)
                actions.clickp('res\del.png', 0.95)
                print(txt_tables[index] + '----------------------已完成')
            break
        except Exception as e:
            logging.error("******error******" + str(e))
            print("\r", '没有找到目标', end="", flush=True)


def qwqf_zb(coordinates):
    print("开始群发")
    
    try:
        x1 = int(coordinates.get('x1', '0'))
        y1 = int(coordinates.get('y1', '0'))
        x2 = int(coordinates.get('x2', '0'))
        y2 = int(coordinates.get('y2', '0'))
        x3 = int(coordinates.get('x3', '0'))
        y3 = int(coordinates.get('y3', '0'))
    except ValueError:
        logging.error("无效的坐标值，必须为整数")
        print("无效的坐标值，必须为整数")
        return
    count = 1
    num = 0
    txt_tables = utils.readtxt(select())
    time.sleep(5)
    while count:
        try:
            # 检测ESC键或任意Alt键是否被按下
            
            for index in range(len(txt_tables)):
                if isPressed(ESC) or isPressed(ALT_LEFT) or isPressed(ALT_RIGHT):
                    count = 0
                    break
                    print("已通过快捷键终止操作")
                if txt_tables[index] == '':
                    continue
                time.sleep(0.05)
                pyautogui.click(x=x1, y=y1)
                time.sleep(0.05)
                pyperclip.copy(txt_tables[index])
                time.sleep(0.3)
                pyautogui.hotkey('ctrl', 'v')
                time.sleep(0.2)
                pyautogui.click(x=x2, y=y2)
                time.sleep(0.05)
                pyautogui.click(x=x3, y=y3)
                print(txt_tables[index] + '----------------------已完成')
            break
        except Exception as e:
            logging.error("******error******" + str(e))
            print("\r", '没有找到目标', end="", flush=True)


def double_alt():
    # 第一次按下并释放 Alt 键
    pyautogui.keyDown('alt')
    #time.sleep(0.1)  # 短暂延时，模拟实际按键的按下时长，可根据需要调整
    pyautogui.keyUp('alt')

    # 短暂间隔，模拟两次按键之间的时间，可调整
   # time.sleep(0.1)

    # 第二次按下并释放 Alt 键
    pyautogui.keyDown('alt')
    #time.sleep(0.1)
    pyautogui.keyUp('alt')

def qf(txt,checked):
    # 标记是否需要终止循环
    terminate_flag = False
    #print(checked)
    # 定义按键回调函数（按下ESC键时触发）
    def on_terminate_key(e):
        nonlocal terminate_flag
        if e.event_type == 'down' and e.name == 'home':  # 监听ESC键
            terminate_flag = True

    # 注册按键监听器
    keyboard.on_press_key('home', on_terminate_key)
    count = 1
    txt_tables = utils.readtxt(select())
    if checked:
        imgurl = selectimg()
    while count:
        try:

            for index in range(len(txt_tables)):
                # 检查终止标志
                if terminate_flag:
                    keyboard.unhook_all()
                    print("终止")
                    return False
                arr = txt_tables[index].split("-")
                time.sleep(0.3)
                # actions.clickp('res\search3.png', 0.9)
                # time.sleep(0.2)
                double_alt()
                pyperclip.copy(arr[0])
                time.sleep(0.2)

                pyautogui.hotkey('ctrl', 'v')
                time.sleep(0.3)
                pyautogui.moveTo(x=460, y=150, duration=0)
                time.sleep(0.2)
                pyautogui.click()
                time.sleep(0.3)
                pyautogui.moveTo(x=1000, y=950, duration=0)

                if len(arr[0]) >= 3:
                    arr[0] = arr[0][-2:]

                if len(arr) > 1 and arr[1]:
                    qftxt = txt.format(nickname=arr[0], parent=arr[1])
                else:
                    qftxt = txt.format(nickname=arr[0])


                time.sleep(0.3)
                pyautogui.click()
                time.sleep(0.5)
                if checked:
                    utils.copy_image_to_clipboard(imgurl)
                    pyautogui.hotkey('ctrl', 'v')
                pyperclip.copy(qftxt)
                pyautogui.hotkey('ctrl', 'v')
                time.sleep(0.3)
                keyboard.wait('enter')
                print(txt_tables[index] + '----------------------已完成')
                time.sleep(0.3)
            break
        except Exception as e:
            logging.error("******error******" + str(e))
            print("\r", '没有找到目标', end="", flush=True)
    return None


def lbq():
    count = 1
    num = 0
    txt_tables = utils.readtxt(select())
    while count:
        try:
            # 检测ESC键或任意Alt键是否被按下
           
            for index in range(len(txt_tables)):
                if isPressed(ESC) or isPressed(ALT_LEFT) or isPressed(ALT_RIGHT):
                    count = 0
                    print("已通过快捷键终止操作")
                    break
                time.sleep(0.2)
                actions.clickp('res\searching1.png', 0.8)
                time.sleep(0.2)
                pyperclip.copy(txt_tables[index])
                time.sleep(0.2)
                pyautogui.hotkey('ctrl', 'v')
                time.sleep(0.85)
                actions.clickp('res\choose1.png', 0.8)

            break
        except Exception as e:
            logging.error("******error******" + str(e))
            print("\r", '没有找到目标', end="", flush=True)

def lbq_zb(coordinates):
    
    
    # 获取坐标值并转换为整数
    try:
        twoX1 = int(coordinates.get('twoX1', '0'))
        twoY1 = int(coordinates.get('twoY1', '0'))
        twoX2 = int(coordinates.get('twoX2', '0'))
        twoY2 = int(coordinates.get('twoY2', '0'))
    except ValueError:
        logging.error("无效的坐标值，必须为整数")
        print("无效的坐标值，必须为整数")
        return
        
    # 验证坐标值是否合理（可以根据实际屏幕分辨率调整范围）
    if not (0 <= twoX1 <= 3840 and 0 <= twoY1 <= 2160 and 0 <= twoX2 <= 3840 and 0 <= twoY2 <= 2160):
        logging.error("坐标值超出合理范围")
        print("坐标值超出合理范围，请重新设置")
        return
        
    count = 1
    num = 0
    
    # 获取文件路径并检查有效性
    file_path = select()
    if not file_path:
        logging.error("未选择有效的文本文件")
        print("未选择有效的文本文件，请重新选择")
        return
    
    # 尝试读取文件内容
    try:
        txt_tables = utils.readtxt(file_path)
        if not txt_tables:
            logging.warning("选择的文件为空")
            print("选择的文件为空，请选择包含内容的文本文件")
            return
        else:
            print(f"成功读取 {len(txt_tables)} 条记录")
    except Exception as e:
        logging.error(f"读取文件失败: {str(e)}")
        print(f"读取文件失败: {str(e)}")
        return
    time.sleep(5)    
    while count:
        try:
            
            for index in range(len(txt_tables)):
                try:
                    if isPressed(ESC):
                        count = 0
                        break
                    time.sleep(0.2)
                    print(f"正在处理第{index+1}条记录: {txt_tables[index]}")
                    pyautogui.click(x=twoX1, y=twoY1)
                    time.sleep(0.2)
                    pyperclip.copy(txt_tables[index])
                    time.sleep(0.3)
                    pyautogui.hotkey('ctrl', 'v')
                    time.sleep(1)
                    pyautogui.click(x=twoX2, y=twoY2)
                except Exception as e:
                    logging.error(f"执行第{index+1}条记录时出错: {str(e)}")
                    print(f"执行第{index+1}条记录时出错: {str(e)}")
                    # 可以选择继续处理下一条或中断
                    # break  # 取消注释以在出错时中断循环

            break
        except Exception as e:
            logging.error("******error******" + str(e))
            print("\r", '出现错误', end="", flush=True)

def jwqgg(coordinates):
    # 导入必要的模块
    import sys
    
    # 设置标准输出的默认编码为UTF-8，避免Windows系统上的编码问题
    if hasattr(sys.stdout, 'reconfigure'):
        try:
            sys.stdout.reconfigure(encoding='utf-8')
            sys.stderr.reconfigure(encoding='utf-8')
        except:
            pass
    
    try:
        sixX1 = int(coordinates.get('sixX1', '0'))
        sixY1 = int(coordinates.get('sixY1', '0'))
        sixX2 = int(coordinates.get('sixX2', '0'))
        sixY2 = int(coordinates.get('sixY2', '0'))
        sixX3 = int(coordinates.get('sixX3', '0'))
        sixY3 = int(coordinates.get('sixY3', '0'))
        sixX4 = int(coordinates.get('sixX4', '0'))
        sixY4 = int(coordinates.get('sixY4', '0'))
        sixX5 = int(coordinates.get('sixX5', '0'))
        sixY5 = int(coordinates.get('sixY5', '0'))
        sixX6 = int(coordinates.get('sixX6', '0'))
        sixY6 = int(coordinates.get('sixY6', '0'))
        
        # 安全打印函数，避免编码错误
        def safe_print(*args, **kwargs):
            """安全地打印内容，处理Unicode字符编码问题"""
            try:
                # 尝试使用UTF-8编码打印
                print(*args, **kwargs)
            except UnicodeEncodeError:
                # 如果失败，尝试逐个字符处理
                text = ' '.join(map(str, args))
                safe_text = ''.join(char if ord(char) < 128 else f'[U+{ord(char):04X}]' for char in text)
                print(safe_text)
                
        # 安全日志函数，避免编码错误
        def safe_log_error(*args, **kwargs):
            """安全地记录错误日志，处理Unicode字符编码问题"""
            try:
                logging.error(*args, **kwargs)
            except UnicodeEncodeError:
                text = ' '.join(map(str, args))
                safe_text = ''.join(char if ord(char) < 128 else f'[U+{ord(char):04X}]' for char in text)
                logging.error(safe_text)
        
        # 安全获取公告内容，确保正确处理Unicode字符
        announcement_content = coordinates.get('announcementContent', '')
        if not announcement_content:
            safe_print("警告: 公告内容为空")
        else:
            # 验证公告内容是否包含Unicode字符
            try:
                # 确保announcement_content是Unicode字符串
                if isinstance(announcement_content, bytes):
                    announcement_content = announcement_content.decode('utf-8', errors='replace')
                
                # 尝试对公告内容进行Unicode编码和解码，确保能正确处理表情符号
                test_encoded = announcement_content.encode('utf-8')
                test_decoded = test_encoded.decode('utf-8')
                
                safe_print(f"公告内容包含 {len(announcement_content)} 个字符，其中可能包含表情符号")
                # 记录字符范围信息，帮助调试
                char_ranges = {}
                for char in announcement_content:
                    char_code = ord(char)
                    char_range = f"{char_code // 1000}xxx"
                    char_ranges[char_range] = char_ranges.get(char_range, 0) + 1
                safe_print(f"字符范围分布: {char_ranges}")
            except Exception as e:
                safe_print(f"公告内容编码检查失败: {str(e)}")
                safe_log_error(f"公告内容编码检查失败: {str(e)}")
        
         # 标记是否需要终止循环
        terminate_flag = False
        
        # 定义按键回调函数（按下ESC键时触发）
        def on_terminate_key(e):
            nonlocal terminate_flag
            if e.event_type == 'down' and e.name == 'alt':  
                terminate_flag = True

        # 注册按键监听器
        keyboard.on_press_key('home', on_terminate_key)
        count = 1
        
        # 尝试读取文本文件，增加错误处理
        try:
            # 先获取文件路径，并检查是否有效
            file_path = select()
            if not file_path or not os.path.isfile(file_path):
                print("警告: 未选择有效的文件或文件不存在")
                keyboard.unhook_all()
                return False
            
            # 读取文件内容
            txt_tables = utils.readtxt(file_path)
            if not txt_tables:
                print("警告: 未读取到任何表格数据")
                keyboard.unhook_all()
                return False
            
            print(f"成功读取文件: {file_path}，共 {len(txt_tables)} 行数据")
        except Exception as e:
            print(f"读取文件时出错: {str(e)}")
            logging.error(f"读取文件时出错: {str(e)}")
            keyboard.unhook_all()
            return False
        
        while count:
            try:
                for index in range(len(txt_tables)):
                    # 检查终止标志
                    if terminate_flag:
                        keyboard.unhook_all()
                        print("终止")
                        return False
                    
                    try:
                        arr = txt_tables[index].split("-")
                    except Exception as e:
                        print(f"解析文本行时出错: {str(e)}")
                        continue
                    
                    time.sleep(0.3)
                    double_alt()
                    
                    # 安全复制第一个元素到剪贴板
                    try:
                        if arr:
                            # 确保arr[0]是UTF-8编码的字符串
                            text_to_copy = str(arr[0])
                            pyperclip.copy(text_to_copy)
                        else:
                            pyperclip.copy("")
                    except Exception as e:
                        safe_print(f"复制arr[0]到剪贴板时出错: {str(e)}")
                    
                    time.sleep(0.5)
                    pyautogui.hotkey('ctrl', 'v')
                    time.sleep(0.5)
                    #点击第一个
                    pyautogui.moveTo(x=sixX2, y=sixY2, duration=0)
                    time.sleep(0.2)
                    pyautogui.click()
                    time.sleep(0.5)
                    #点击群公告
                    pyautogui.moveTo(x=sixX3, y=sixY3, duration=0)
                    time.sleep(0.3)
                    pyautogui.click()
                    #点击编辑
                    pyautogui.moveTo(x=sixX4, y=sixY4, duration=0)
                    time.sleep(0.2)
                    pyautogui.click()
                    time.sleep(0.3)
                
                    # 安全处理公告内容，确保表情符号能被正确保留
                    try:
                        # 先确保announcement_content是字符串格式，使用Unicode编码
                        if isinstance(announcement_content, bytes):
                            safe_content = announcement_content.decode('utf-8', errors='replace')
                        else:
                            safe_content = str(announcement_content)
                        
                        # 检查是否包含需要的占位符，如果没有则直接使用原内容
                        if '{link}' not in safe_content:
                            qftxt = safe_content
                        else:
                            # 安全格式化处理，确保占位符替换不会出错
                            try:
                                # 确保链接文本是UTF-8编码的
                                link_text = arr[1] if len(arr) >= 2 else "[链接]"
                                link_text = str(link_text)
                                
                                # 格式化内容
                                qftxt = safe_content.format(link=link_text)
                            except KeyError as e:
                                safe_print(f"格式化时缺少占位符: {str(e)}")
                                # 替换不存在的占位符为空白
                                import re
                                qftxt = re.sub(r'{[^}]+}', '', safe_content)
                            except Exception as e:
                                safe_print(f"格式化公告内容时出错: {str(e)}")
                                qftxt = safe_content.replace('{link}', '[链接]')
                    except Exception as e:
                        # 如果格式化失败，使用原始公告内容
                        safe_print(f"处理公告内容时出错: {str(e)}，使用原始内容")
                        qftxt = str(announcement_content)
                    
                    # 记录处理后的公告内容信息（不记录完整内容，避免编码错误）
                    try:
                        safe_print(f"处理后公告内容长度: {len(qftxt)} 字符")
                    except:
                        safe_print("处理后公告内容信息无法显示")
                    
                    # 复制到剪贴板，确保Unicode字符（包括表情符号）能被正确处理
                    clipboard_success = False
                    
                    # 方法1：优先使用pyperclip
                    try:
                        # 确保pyperclip能处理Unicode
                        pyperclip.copy(qftxt)
                        clipboard_success = True
                    except Exception as e:
                        safe_print(f"使用pyperclip复制到剪贴板时出错: {str(e)}")
                        
                        # 方法2：尝试使用win32clipboard的Unicode支持
                        try:
                            import win32clipboard
                            win32clipboard.OpenClipboard()
                            win32clipboard.EmptyClipboard()
                            # 明确使用CF_UNICODETEXT格式确保表情符号正确复制
                            win32clipboard.SetClipboardText(qftxt, win32clipboard.CF_UNICODETEXT)
                            win32clipboard.CloseClipboard()
                            clipboard_success = True
                        except Exception as inner_e:
                            safe_print(f"使用win32clipboard复制也失败: {str(inner_e)}")
                            
                            # 方法3：作为最后的备选方案，尝试使用更简单的方式
                            try:
                                # 先清空输入区域
                                pyautogui.hotkey('ctrl', 'a')
                                pyautogui.press('delete')
                                time.sleep(0.2)
                                # 使用pyautogui直接输入文本
                                pyautogui.write(qftxt, interval=0.01)  # 添加小间隔提高可靠性
                                safe_print("使用pyautogui直接输入文本")
                                clipboard_success = True
                            except Exception as final_e:
                                safe_print(f"所有复制方法均失败: {str(final_e)}")
                
                    # 全选并粘贴（如果剪贴板操作成功且没有使用直接输入）
                    if clipboard_success and not pyautogui.write in locals():
                        try:
                            pyautogui.hotkey('ctrl', 'a')
                            time.sleep(0.2)
                            pyautogui.hotkey('ctrl', 'v')
                            time.sleep(0.3)
                        except Exception as e:
                            safe_print(f"粘贴操作失败: {str(e)}")
                     
                    #点击发布
                    try:
                        pyautogui.moveTo(x=sixX5, y=sixY5, duration=0)
                        time.sleep(0.2)
                        pyautogui.click()
                        time.sleep(0.3)
                    except Exception as e:
                        print(f"点击发布按钮失败: {str(e)}")
                    
                    # 等待用户按下Enter键继续
                    try:
                        keyboard.wait('enter')
                        # 使用安全打印，避免编码错误
                        try:
                            safe_print(f"{txt_tables[index]}----------------------已完成")
                        except:
                            safe_print(f"第{index+1}行----------------------已完成")
                    except Exception as e:
                        safe_print(f"等待Enter键时出错: {str(e)}")
                    
                    time.sleep(1.2)
                break
            except Exception as e:
                safe_log_error(f"******jwqgg循环内错误******{str(e)}")
                safe_print(f"\r操作过程中出错: {str(e)}", end="", flush=True)
                # 短暂暂停后继续尝试
                time.sleep(1)
            finally:
                # 确保无论如何都会释放按键监听器
                keyboard.unhook_all()
                pass
    except Exception as e:
        safe_log_error(f"******jwqgg函数整体错误******{str(e)}")
        safe_print(f"jwqgg函数执行失败: {str(e)}")
        # 确保释放按键监听器
        try:
            keyboard.unhook_all()
        except:
            pass
    return None


class AutoRun():
    def __init__(self):
        # 创建实例时不自动执行，而是提供方法供调用
        pass
        
    def add_to_startup(self):
        """添加程序到Windows自启动项"""
        name = '核桃夹'  # 要添加的项值名称
        
        # 使用动态路径而不是硬编码路径
        try:
            # 获取当前程序的绝对路径
            if hasattr(sys, 'frozen'):
                # 对于打包后的程序
                current_exe_path = sys.executable
            else:
                # 对于未打包的Python脚本
                current_exe_path = os.path.abspath(__file__)
                
            # 如果是.py文件，使用pythonw.exe来运行
            if current_exe_path.endswith('.py'):
                pythonw_path = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe')
                path = f'"{pythonw_path}" "{current_exe_path}"'  # 带引号以处理路径中的空格
            else:
                path = f'"{current_exe_path}"'  # 带引号以处理路径中的空格
            
            # 注册表项名 - 使用原始字符串处理反斜杠
            KeyName = r'Software\Microsoft\Windows\CurrentVersion\Run'
            
            # 检查是否已经存在自启动项
            try:
                key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_READ)
                existing_value, _ = win32api.RegQueryValueEx(key, name)
                win32api.RegCloseKey(key)
                
                if existing_value == path:
                    print(f'自启动项"{name}"已经存在且路径正确')
                    return True
            except OSError:
                # 键不存在，继续添加
                pass
            
            # 添加到注册表
            key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_SET_VALUE)
            win32api.RegSetValueEx(key, name, 0, win32con.REG_SZ, path)
            win32api.RegCloseKey(key)
            
            print(f'成功添加"{name}"到自启动项')
            return True
        except Exception as e:
            logging.error(f'添加自启动项失败: {str(e)}')
            print(f'添加自启动项失败: {str(e)}')
            return False
            
    def remove_from_startup(self):
        """从Windows自启动项中移除程序"""
        name = '核桃夹'  # 要移除的项值名称
        KeyName = r'Software\Microsoft\Windows\CurrentVersion\Run'
        
        try:
            key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_SET_VALUE)
            win32api.RegDeleteValue(key, name)
            win32api.RegCloseKey(key)
            print(f'成功从自启动项移除"{name}"')
            return True
        except OSError as e:
            if hasattr(e, 'winerror') and e.winerror == 2:  # ERROR_FILE_NOT_FOUND
                print(f'自启动项"{name}"不存在')
            else:
                logging.error(f'移除自启动项失败: {str(e)}')
                print(f'移除自启动项失败: {str(e)}')
            return False
            
    def check_startup_status(self):
        """检查程序是否已经在自启动项中"""
        name = '核桃夹'  # 要检查的项值名称
        KeyName = r'Software\Microsoft\Windows\CurrentVersion\Run'
        
        try:
            key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, KeyName, 0, win32con.KEY_READ)
            existing_value, _ = win32api.RegQueryValueEx(key, name)
            win32api.RegCloseKey(key)
            return True, existing_value
        except OSError:
            return False, None

def select():  # 选择文件
    file_types = ('文本文档 (*.txt)', 'All files (*.*)')
    result = window.create_file_dialog(webview.OPEN_DIALOG, allow_multiple=True, file_types=file_types)
    if result and result[0] and os.path.isfile(result[0]):
        return result[0]
    else:
        logging.warning("未选择有效的文件")
        return ''

def selectimg():  # 选择文件
    file_types = ('群发图片 (*.png)', 'All files (*.*)')
    result = window.create_file_dialog(webview.OPEN_DIALOG, allow_multiple=True, file_types=file_types)
    return result[0] if result else ''

def close():
    time.sleep(1)
    pid = os.getpid()
    os.kill(pid, signal.SIGTERM)

class Api:
    def getteacher(self):
        return teachername

    def creatght(self):
        print("创建规划表")
        mes=ght.creat_ght()
        return mes
    def getstatus(self, text):
        arr = str(text).split("+")
        id1 = user.getcrmid()
        temp_data = {
            "user_id": arr[0],
            "password": '123456'
        }
        start_time = getHomeWork.pushcode(arr[2], id1, temp_data)
        return utils.getstatus(temp_data, start_time)

    def goupdate(self):
        update.goupdate(update.checkVersion()['version'])
        close()

    def update(self):
        res = {'version': "", 'msg': "", 'isMust': False, 'update': False, 'oldversion': ""}
        if update.checkVersion() == -1:
            pass
            close()#------------------------------------------------------------------------------------------------------------
        # res['version'] = update.checkVersion()['version']
        # res['msg'] = update.checkVersion()['update_msg']
        # res['update'] = update.updateApp()
        res['oldversion'] = update.getoldversion()
        #check_update()
        #update.run_updater()
        return res

    def exit(self):
        window.destroy()

    def runclass(self, text) -> str:
        arr = str(text).split("+")
        pyperclip.copy(utils.readformatdata(arr))
        return f'pythonw {text}'

    def useai(self, text) -> str:
        ai_msg = utils.getai(text)
        return ai_msg

    def copyai(self, text) -> str:
        pyperclip.copy(text)
        return ai_msg

    def getworkmsg(self, txt):
        global teachername
        id = utils.readtxt("id.txt")
        id1 = user.getcrmid()
        msg = []

        if id1 != 0 and id1 is not None and id1 != "None":
            start_heart(id1)
            msg.append(utils.getAll(id1))
            teachername=utils.getTeacherName(id1)
            msg.append(teachername)
            return msg
        if len(id) > 0:
            if status:
                start_heart(id[0])
            msg.append(utils.getAll(id[0]))
            teachername = utils.getTeacherName(id[0])
            msg.append(teachername)
            return msg
        elif txt != "":
            utils.writetxt("id.txt", txt)
            id = utils.readtxt("id.txt")
            if status:
                start_heart(id[0])
            msg.append(utils.getAll(id[0]))
            return msg
        else:
            return ""

    def qf(self, program) -> str:
        window.minimize()
        qwqf()
        return f'pythonw {program}'

    def greatqf(self,txt) -> str:
        # txt_content = txt.get('txt', '')  # 获取 txt 字段，默认空字符串
        # is_checked = txt.get('check', False)
        txt_content = urllib.parse.unquote(txt['txt'])
        is_checked = txt['check']
        print(txt)
        window.minimize()
        qf(txt_content,is_checked)
        window.restore()
        return ""

    def getVersion(self):
        ver_tables = utils.readtxt('version.htj')
        version = ver_tables[len(ver_tables) - 1][8:12]
        return version

    def zq(self, program) -> str:
        window.minimize()
        qwqf()
        print('运行')
        return f'pythonw {program}'

    def xh(self, program) -> str:
        window.minimize()
        lbq()
        print('运行')
        return f'pythonw {program}'

    def shown(self) -> str:
        print('窗口显示')
        window.restore()
        return ''

    def quit(self) -> str:
        window.hide()
        return ''

    def min(self):
        window.minimize()
        return ''

    def shutdown(self):
        print('关闭！')
        with open('version.htj', 'a') as f:
            f.write("\nVersion-#")  # 文件的写操作
        sys.exit(0)
        window.destroy()
        return ''

    def rmsg(self, text, header):
        message.pc_toast(header, text)
        if text == '提醒已停用':
            message.sched.shutdown(wait=False)
        else:
            message.sched.start()
        print(text)
        return ''

    def save_coordinates(self, coordinates):
        # 接收三个坐标点的信息
        print(f"收到三个坐标点信息: {coordinates}")
        # 这里可以添加处理坐标的逻辑，比如保存到文件或执行相应操作
        qwqf_zb(coordinates)
        # 返回处理结果
        return f"坐标信息已接收"
        
    def save_two_coordinates(self, coordinates):
        # 接收两个坐标点的信息
        print(f"收到两个坐标点信息: {coordinates}")
        lbq_zb(coordinates)
        # 这里可以添加处理坐标的逻辑，比如保存到文件或执行相应操作
        
    def save_six_coordinates(self, coordinates):
        # 接收六个坐标点的信息
        try:
            print(f"收到六个坐标点信息: {coordinates}")
        except UnicodeEncodeError:
            # 如果无法在终端打印完整坐标信息，记录到日志
            logging.info(f"收到六个坐标点信息，包含可能的表情符号")
        
        # 验证并获取六个坐标值
        try:
            sixX1 = int(coordinates.get('sixX1', '0'))
            sixY1 = int(coordinates.get('sixY1', '0'))
            sixX2 = int(coordinates.get('sixX2', '0'))
            sixY2 = int(coordinates.get('sixY2', '0'))
            sixX3 = int(coordinates.get('sixX3', '0'))
            sixY3 = int(coordinates.get('sixY3', '0'))
            sixX4 = int(coordinates.get('sixX4', '0'))
            sixY4 = int(coordinates.get('sixY4', '0'))
            sixX5 = int(coordinates.get('sixX5', '0'))
            sixY5 = int(coordinates.get('sixY5', '0'))
            sixX6 = int(coordinates.get('sixX6', '0'))
            sixY6 = int(coordinates.get('sixY6', '0'))
            
            # 解析公告内容
            announcement_content = coordinates.get('announcementContent', '')
            # 安全处理公告内容，避免编码问题
            if announcement_content:
                try:
                    # 确保内容是字符串并进行安全处理
                    if isinstance(announcement_content, bytes):
                        safe_announcement = announcement_content.decode('utf-8', errors='replace')
                    else:
                        safe_announcement = str(announcement_content)
                    
                    # 验证内容能正确编码
                    test_encoding = safe_announcement.encode('utf-8')
                    
                    # 使用try-except避免打印时的编码错误
                    try:
                        print(f"收到公告内容，长度: {len(safe_announcement)} 字符")
                    except UnicodeEncodeError:
                        # 如果无法在终端打印，就记录到日志
                        logging.info(f"收到公告内容，长度: {len(safe_announcement)} 字符，其中可能包含表情符号")
                    
                    # 可以在这里添加保存公告内容的逻辑
                    # 例如，保存到文件或数据库
                except Exception as e:
                    try:
                        logging.error(f"处理公告内容时出错: {str(e)}")
                        print(f"处理公告内容时出错: {str(e)}")
                    except UnicodeEncodeError:
                        logging.error(f"处理公告内容时出错: {repr(e)}")
        except ValueError:
            logging.error("无效的坐标值，必须为整数")
            print("无效的坐标值，必须为整数")
            return "无效的坐标值，必须为整数"
        
        # 验证坐标值是否合理
        if not (0 <= sixX1 <= 3840 and 0 <= sixY1 <= 2160 and 
                0 <= sixX2 <= 3840 and 0 <= sixY2 <= 2160 and 
                0 <= sixX3 <= 3840 and 0 <= sixY3 <= 2160 and 
                0 <= sixX4 <= 3840 and 0 <= sixY4 <= 2160 and 
                0 <= sixX5 <= 3840 and 0 <= sixY5 <= 2160 and 
                0 <= sixX6 <= 3840 and 0 <= sixY6 <= 2160):
            logging.error("坐标值超出合理范围")
            print("坐标值超出合理范围，请重新设置")
            return "坐标值超出合理范围，请重新设置"
        
        # 这里可以添加教务群公告相关的操作逻辑
        # 例如，可以创建一个专门的函数来处理六个坐标点的操作
        print(f"六个坐标点已验证通过，准备执行操作")
        jwqgg(coordinates)
        # 返回处理结果，包括是否成功接收到公告内容
        if announcement_content:
            return "六个坐标信息和公告内容已接收"
        else:
            return "六个坐标信息已接收"
    
    def process_guihua_request(self, data):
        # 接收规划请求
        input_content = data.get('input', '')
        # 使用try-except捕获可能的编码错误
        try:
            print(f"收到规划请求")
            # 安全打印输入内容的长度而不是内容本身，避免编码问题
            print(f"输入内容长度: {len(input_content)} 字符")
        except Exception:
            # 如果打印失败，使用更安全的方式
            print("收到规划请求，但内容包含无法打印的字符")
        
        # 验证输入内容是否包含{nickname}
        if '{nickname}' not in input_content:
            return "输入内容必须包含{nickname}！"
        
        try:
            # 导入必要的模块
            import os
            import pyperclip
            import keyboard
            import time
            import pyautogui
            import utils
            import sys
            
            # 获取桌面路径
            desktop_path = os.path.join(os.path.expanduser("~"), "Desktop")
            
            # 构建规划图文件夹路径
            guihua_folder = os.path.join(desktop_path, "规划图")
            
            # 检查规划图文件夹是否存在
            if not os.path.exists(guihua_folder):
                return "桌面上未找到'规划图'文件夹！"
            
            # 读取文件夹中的所有文件名
            files = []
            for file in os.listdir(guihua_folder):
                try:
                    # 安全地处理文件名，避免编码问题
                    safe_file = str(file) if not isinstance(file, str) else file
                    file_path = os.path.join(guihua_folder, safe_file)
                    if os.path.isfile(file_path):
                        files.append(safe_file)
                except Exception:
                    continue  # 跳过无法处理的文件
            
            if not files:
                return "'规划图'文件夹中没有找到文件！"
            
            # 遍历所有图片
            image_count = 0
            for file in files:
                # 检查文件是否为图片（根据扩展名判断）
                image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
                try:
                    file_ext = os.path.splitext(file)[1].lower()
                    if file_ext in image_extensions:
                        image_count += 1
                        # 安全打印图片名，不直接在控制台打印可能包含特殊字符的文件名
                        print(f"处理第{image_count}张图片")
                        
                        try:
                            # 将文件名作为文本{nickname}的参数
                            # 提取文件名（不包含扩展名）作为nickname
                            nickname = os.path.splitext(file)[0]
                            if len(nickname) >= 3:
                                nickname = nickname[-2:]
                            
                            # 确保nickname可以安全处理
                            safe_nickname = str(nickname) if not isinstance(nickname, str) else nickname
                            
                            double_alt()
                            # 安全地复制到剪贴板
                            pyperclip.copy(safe_nickname)
                            time.sleep(0.5)

                            pyautogui.hotkey('ctrl', 'v')
                            time.sleep(0.7)
                            
                            # 替换输入内容中的{nickname}为实际文件名，使用安全的方式处理
                            try:
                                message_text = input_content.replace('{nickname}', safe_nickname)
                            except Exception:
                                # 如果替换失败，使用原始文本
                                message_text = input_content
                                print(f"警告: 无法替换nickname，但继续处理")
                            
                            # 移动鼠标到指定位置
                            pyautogui.moveTo(x=460, y=178, duration=0)
                            pyautogui.click()  # 点击第一个位置
                            time.sleep(0.5)  # 等待一下以确保操作完成
                            
                            # 移动鼠标到第二个位置
                            pyautogui.moveTo(x=1000, y=920, duration=0)
                            pyautogui.click()  # 点击第二个位置
                            time.sleep(0.5)  # 等待一下以确保操作完成
                            
                            # 获取图片的完整路径
                            image_path = os.path.join(guihua_folder, file)
                            
                            # 复制图片到剪贴板
                            if hasattr(utils, 'copy_image_to_clipboard'):
                                success = utils.copy_image_to_clipboard(image_path)
                                if success:
                                    print(f"成功复制图片到剪贴板")
                                    # 粘贴图片
                                    pyautogui.hotkey('ctrl', 'v')
                                    time.sleep(1)  # 等待图片粘贴完成
                                else:
                                    print(f"复制图片到剪贴板失败")
                            else:
                                print("utils模块中没有找到copy_image_to_clipboard函数")
                                
                            # 粘贴文本（替换后的消息）
                            try:
                                # 进一步安全处理消息文本，避免编码问题
                                safe_message = str(message_text) if not isinstance(message_text, str) else message_text
                                pyperclip.copy(safe_message)
                                pyautogui.hotkey('ctrl', 'v')
                                time.sleep(1)  # 等待文本粘贴完成
                            except Exception as e:
                                print(f"警告: 无法粘贴文本，但继续处理")
                        
                            keyboard.wait('enter')
                            # 可以添加一个短暂的延迟，确保操作顺序执行
                            time.sleep(1)
                            
                        except Exception as e:
                            # 安全地处理异常，避免在转换异常信息时出现编码问题
                            try:
                                error_msg = str(e)
                            except:
                                error_msg = "未知错误"
                            print(f"处理图片时出错: {error_msg}")
                except Exception:
                    continue  # 跳过无法处理的文件扩展名检查
                    
            # 安全地返回结果，避免在格式化字符串时出现编码问题
            try:
                return f"成功处理了{image_count}张图片，共读取到{len(files)}个文件！"
            except:
                return f"成功处理了{image_count}张图片，共读取到{len(files)}个文件！"  # 使用简单的字符串避免编码问题
        except Exception as e:
            # 安全地处理异常，避免在转换异常信息时出现编码问题
            try:
                error_msg = str(e)
                # 过滤掉可能包含特殊字符的错误信息
                if isinstance(error_msg, str):
                    # 只保留ASCII字符和常见的中文，移除其他特殊字符
                    safe_error = ''.join([c for c in error_msg if ord(c) < 128 or (0x4e00 <= ord(c) <= 0x9fff)])
                    return f"处理请求时出错: {safe_error}"
                else:
                    return "处理请求时出现未知错误"
            except:
                return "处理请求时出现未知错误（Unicode编码问题）" 
        return "坐标信息已接收"

chinese = {
    'global.quitConfirmation': u'确定关闭?',
}

# 单实例检测代码
# 创建一个唯一命名的互斥体
mutex_name = 'hetaojia_app_mutex'
mutex = win32event.CreateMutex(None, False, mutex_name)

# 检查是否已经有实例在运行
# 使用数值183代替win32con.ERROR_ALREADY_EXISTS，解决某些pywin32版本中该常量不存在的问题
if win32api.GetLastError() == 183:
    # 有实例在运行，查找并唤起已有的窗口
    print('检测到程序已有实例在运行，正在唤起...')
    
    # 查找窗口并设置为前台
    def find_and_bring_window():        
        # 尝试使用EnumWindows来查找窗口
        def callback(hwnd, extra):
            if win32gui.IsWindowVisible(hwnd):
                title = win32gui.GetWindowText(hwnd)
                if '核桃夹' in title:
                    # 窗口存在，设置为前台
                    win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)  # 恢复窗口（如果最小化）
                    win32gui.SetForegroundWindow(hwnd)  # 设为前台窗口
                    return False  # 停止枚举
            return True
        
        try:
            import win32gui
            win32gui.EnumWindows(callback, None)
        except Exception as e:
            print(f'查找窗口时出错: {e}')
    
    find_and_bring_window()
    sys.exit(0)  # 退出当前实例

auto = AutoRun()
# 调用方法添加自启动项
auto.add_to_startup()
api = Api()

window = webview.create_window(
    title='核桃夹',
    url='F:\\hetaojia\\res\\index.html',
    #url='C:\\Program Files (x86)\\核桃夹\\res\\index.html',
    #url=hurl,
    width=1100,
    height=650,
    on_top=False,
    resizable=True,  # 固定窗口大小
    text_select=False,  # 禁止选择文字内容
    confirm_close=False,  # 关闭时提示
    js_api=api,  # api中定义供html调用的函数
    min_size=(1200, 750),  # 设置窗口最小尺寸
    frameless=True,  # 创建一个无框窗口
    background_color='#FFFFFF',
)
window.events.closed += on_closed
window.events.closing += on_closing
# window.events.shown += on_shown
window.events.minimized += on_minimized
window.events.maximized += on_maximized
window.events.restored += on_restored


# 定义检查更新函数
def check_update():
    try:
        import sys
        # 获取当前可执行文件的路径，解决打包成exe后路径不正确的问题
        if getattr(sys, 'frozen', False):
            # 已打包成可执行文件
            current_dir = os.path.dirname(sys.executable)
            print(f"运行环境: 已打包的可执行文件")
            print(f"可执行文件路径: {sys.executable}")
            print(f"可执行文件所在目录: {current_dir}")
        else:
            # 从脚本直接运行
            current_dir = os.path.dirname(os.path.abspath(__file__))
            print(f"运行环境: 脚本直接运行")
            print(f"脚本文件所在目录: {current_dir}")
        
        # 拼接updater.exe的完整路径
        updater_path = os.path.join(current_dir, 'updater.exe')
        print(f"尝试查找更新程序路径: {updater_path}")
        
        # 检查updater.exe是否存在
        if os.path.exists(updater_path):
            print(f"找到更新程序: {updater_path}")
            # 使用ctypes以管理员权限启动updater.exe
            import ctypes
            if sys.platform.startswith('win'):
                # 请求管理员权限运行updater.exe
                # SW_SHOWNORMAL = 1 表示正常显示窗口
                try:
                    result = ctypes.windll.shell32.ShellExecuteW(
                        None, "runas", updater_path, "", current_dir, 1
                    )
                    # ShellExecuteW 返回大于32表示成功
                    if result > 32:
                        print(f"已以管理员权限启动更新程序，返回值: {result}")
                    else:
                        print(f"以管理员权限启动更新程序失败，返回值: {result}")
                        # 如果失败，尝试普通方式启动
                        print("尝试使用普通方式启动更新程序...")
                        subprocess.Popen([updater_path], cwd=current_dir)
                except Exception as inner_e:
                    print(f"以管理员权限启动更新程序异常: {inner_e}")
                    # 如果失败，尝试普通方式启动
                    print("尝试使用普通方式启动更新程序...")
                    subprocess.Popen([updater_path], cwd=current_dir)
            else:
                # 非Windows系统，使用普通方式启动
                subprocess.Popen([updater_path], cwd=current_dir)
        else:
            print(f"未找到更新程序: {updater_path}")
            # 尝试在当前工作目录查找
            cwd = os.getcwd()
            cwd_updater_path = os.path.join(cwd, 'updater.exe')
            if os.path.exists(cwd_updater_path):
                print(f"在当前工作目录找到更新程序: {cwd_updater_path}")
                # 尝试启动
                import ctypes
                if sys.platform.startswith('win'):
                    try:
                        ctypes.windll.shell32.ShellExecuteW(
                            None, "runas", cwd_updater_path, "", cwd, 1
                        )
                        print("已从当前工作目录以管理员权限启动更新程序")
                    except Exception as e:
                        print(f"启动失败，尝试普通方式: {e}")
                        subprocess.Popen([cwd_updater_path], cwd=cwd)
                else:
                    subprocess.Popen([cwd_updater_path], cwd=cwd)
    except Exception as e:
        print(f"启动更新程序时出错: {e}")
        # 记录错误到日志文件
        try:
            with open(os.path.join(os.path.expanduser("~") , "hetaojia_error.log"), "a") as log_file:
                import datetime
                log_file.write(f"[{datetime.datetime.now()}] 更新程序启动错误: {str(e)}\n")
        except:
            pass

class MainView:
    # 添加双击检测变量
    last_click_time = 0
    click_count = 0
    double_click_threshold = 0.3  # 双击时间阈值（秒）
    
    def on_mouse_clicked(icon, item):
        import time
        current_time = time.time()
        
        # 检测双击
        if current_time - MainView.last_click_time < MainView.double_click_threshold:
            MainView.click_count += 1
        else:
            MainView.click_count = 1
        
        MainView.last_click_time = current_time
        
        # 如果检测到双击，最大化窗口
        if MainView.click_count == 2:
            print("双击系统托盘，最大化窗口")
            window.restore()  # 先恢复窗口（如果最小化）
            window.maximize()  # 然后最大化窗口
            MainView.click_count = 0  # 重置点击计数

    def creatv(self):
        webview.start( localization=chinese, http_port=4399)

    def closemp(self):
        global status
        window.destroy()
        status = False

    def creati(self):
        # 加载图标
        ico = Image.open("res/maple.ico")
        
        # 定义检查更新函数
        check_update()
        
        # 定义显示版本信息函数
        def show_version():
            try:
                old_version = update.getoldversion()
                print(f"核桃夹版本: {old_version}")
                # 这里可以添加更复杂的关于对话框
            except Exception as e:
                print(f"获取版本信息时出错: {e}")
        
        # 创建菜单
        menu = Menu(
            MenuItem('打开', window.show, default=True),
            MenuItem('最小化到托盘', window.minimize),
            Menu.SEPARATOR,
            MenuItem('检查更新', check_update),
            MenuItem('关于核桃夹', show_version),
            Menu.SEPARATOR,
            MenuItem('退出', self.closemp)
        )
        
        # 创建并运行托盘图标
        icon = Icon("hetaojia", ico, "核桃夹助手", menu)
        icon.run()

m = MainView()
threading.Thread(target=message.sched.start).start()
threading.Thread(target=m.creati, daemon=True).start()
m.creatv()

# 程序退出时关闭互斥体
if mutex:
    win32api.CloseHandle(mutex)